package rest

import (
	"archive/tar"
	"errors"
	"io"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"

	"gitee.com/hexug/go-chain-restful-client/common/logger"
)

type Method int8

const (
	GET Method = iota
	POST
	HEAD
	PUT
	DELETE
	CONNECT
	OPTIONS
	TRACE
	PATCH
)

var MethodMap = map[Method]string{
	GET:     "GET",
	POST:    "POST",
	HEAD:    "HEAD",
	PUT:     "PUT",
	DELETE:  "DELETE",
	CONNECT: "CONNECT",
	OPTIONS: "OPTIONS",
	TRACE:   "TRACE",
	PATCH:   "PATCH",
}

func (s Method) MarshalJSON() ([]byte, error) {
	v, ok := MethodMap[s]
	if ok {
		return []byte(`"` + v + `"`), nil
	} else {
		return nil, nil
	}
}
func (s *Method) UnmarshalJSON(data []byte) error {
	str := string(data)
	str = strings.Trim(str, `"`)
	for k, v := range MethodMap {
		if v == strings.ToUpper(str) {
			*s = k
			return nil
		}
	}
	return nil
}

func (s Method) String() string {
	v, ok := MethodMap[s]
	if ok {
		return v
	}
	return ""
}
func NewRequest(c *Client) *Request {
	r := &Request{
		c:           c,
		timeout:     c.client.Timeout,
		basePath:    c.baseURL,
		headers:     c.headers,
		cookies:     c.cookies,
		authType:    c.authType,
		user:        c.user,
		token:       c.token,
		log:         c.log,
		proxyAdd:    c.proxyAdd,
		proxyPort:   c.proxyPort,
		proxyScheme: c.proxyScheme,
	}
	return r
}

type Request struct {
	c *Client

	log     *logger.Log
	timeout time.Duration

	authType AuthType
	user     *User
	token    string

	method Method
	//前缀路径
	prePath string
	//后缀路径
	subPath string
	//基础URL部分
	basePath string
	//请求的完整路径
	reqPath       string
	isAbs         bool
	cookies       []*http.Cookie
	headers       http.Header
	params        url.Values
	formData      url.Values
	multiFormData url.Values
	files         map[string]string
	rowText       string
	dataJs        []byte
	body          io.Reader

	err error

	//代理部分
	proxyAdd    string //代理地址
	proxyPort   string //代理端口
	proxyScheme string //代理协议
}

// 打包目录下的所有文件和子文件，但不包括当前目录
func tarFolderToReader(
	tarWriter *tar.Writer,
	folderPath string,
	rootDir bool,
	fileWhitelist,
	dirWhitelist,
	fileBlacklist,
	dirBlacklist []string,
) error {
	err := filepath.Walk(folderPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if path == folderPath {
			// 跳过根目录
			return nil
		}
		for _, f1 := range fileWhitelist {
			for _, f2 := range fileBlacklist {
				if filepath.Clean(f1) == filepath.Clean(f2) {
					return errors.New("文件的黑白名单冲突异常")
				}
			}
		}
		for _, d1 := range dirWhitelist {
			for _, d2 := range dirBlacklist {
				if filepath.Clean(d1) == filepath.Clean(d2) {
					return errors.New("文件夹的黑白名单冲突异常")
				}
			}
		}
		// 如果文件白名单不为空，且是个文件
		if len(fileWhitelist) != 0 && info.Mode().IsRegular() {
			flagfile := false
			//遍历白名单
			for _, filew := range fileWhitelist {
				// 如果匹配到白名单，就直接打包
				if filepath.Clean(filew) == filepath.Clean(path) {
					flagfile = true
					goto end
				} else if len(dirWhitelist) != 0 {
					//如果没匹配到白名单，就看下所在的文件夹是不是被允许的
					for _, dirw := range dirWhitelist {
						if strings.HasPrefix(filepath.Clean(path), filepath.Clean(dirw)) {
							//如果是文件夹白名单中的文件，就需要判断在不在文件黑名单中
							if len(fileBlacklist) != 0 {
								for _, filew := range fileBlacklist {
									if filepath.Clean(filew) == filepath.Clean(path) {
										goto end
									}
								}
							}
							if len(dirBlacklist) != 0 {
								for _, dirw := range dirBlacklist {
									if strings.HasPrefix(filepath.Clean(path), filepath.Clean(dirw)) {
										goto end
									}
								}
							}
							flagfile = true
							goto end
						}
					}

				} else if len(fileBlacklist) != 0 {
					return errors.New("文件的黑白名单冲突异常")
				}
			}
		end:
			if !flagfile {
				return nil
			}
		} else if len(fileBlacklist) != 0 && info.Mode().IsRegular() {
			flagfile := false
			for _, filew := range fileBlacklist {
				if filepath.Clean(filew) == filepath.Clean(path) {
					flagfile = true
					break
				}
			}
			if len(dirBlacklist) != 0 {
				for _, dirw := range dirBlacklist {
					if strings.HasPrefix(filepath.Clean(path), filepath.Clean(dirw)) {
						flagfile = true
						break
					}
				}
			}
			if flagfile {
				return nil
			}
		}
		// 如果文件夹白名单存在，且是个文件夹
		if len(dirWhitelist) != 0 && info.Mode().IsDir() {
			flagdir := false
			for _, dirw := range dirWhitelist {
				// 判断是不是在白名单中
				if filepath.Clean(dirw) == filepath.Clean(path) {
					//是在白名单，则直接跳出通过
					flagdir = true
					goto end1
				} else if strings.HasPrefix(filepath.Clean(path), filepath.Clean(dirw)) {
					if len(dirBlacklist) != 0 {
						for _, dirw := range dirBlacklist {
							if filepath.Clean(dirw) == filepath.Clean(path) {
								goto end1
							}
						}
					}
					flagdir = true
					goto end1
				}
			}
		end1:
			if !flagdir {
				return nil
			}
		} else if len(dirBlacklist) != 0 && info.Mode().IsDir() {
			flagdir := false
			for _, dirw := range dirBlacklist {
				if filepath.Clean(dirw) == filepath.Clean(path) {
					flagdir = true
					break
				}
			}
			if flagdir {
				return nil
			}
		}
		header, err := tar.FileInfoHeader(info, "")
		if err != nil {
			return err
		}
		if rootDir {
			header.Name = filepath.FromSlash(path)
		} else {
			// 使用相对路径而不是绝对路径
			relPath, err := filepath.Rel(folderPath, path)
			if err != nil {
				return err
			}
			header.Name = filepath.FromSlash(relPath)
		}

		// 对于Windows，在标头中将反斜杠转换为正斜杠。名称字段
		if os.PathSeparator == '\\' {
			header.Name = filepath.ToSlash(header.Name)
		}

		if info.IsDir() {
			header.Name += "/"
		}
		header.Format = tar.FormatGNU
		if err := tarWriter.WriteHeader(header); err != nil {
			return err
		}

		if !info.Mode().IsRegular() {
			return nil
		}
		file, err := os.Open(path)
		if err != nil {
			return err
		}
		defer file.Close()

		_, err = io.Copy(tarWriter, file)
		return err
	})

	if err != nil {
		return err
	}

	return nil
}
